#!/usr/bin/env python
# encoding: utf-8


"""
@file: sanjiaobudengshi.py
@time: 2017/3/15 下午7:41
"""
# 三角不等式

from mathsolver.functions.base import *
from sympy import sin, cos, pi, tan, FiniteSet, cot, asin, acos, atan
from mathsolver.functions.budengshi import common_opers as co
from mathsolver.functions.sanjiao import sanjiao_utils as su
from mathsolver.functions.mathematica.mathematicaSolve import MathematicaSolve
from mathsolver.functions.sanjiao.huajian import trig_simplify

SIN_PATTERN = 'sin\\(.*?\\)'
COS_PATTERN = 'cos\\(.*?\\)'


# A*sin() < a
class SanJiaoBuDengShi001(BaseFunction):
    # 首先将不等式化简为 A*sin(w*x+α) <= C (C是常数)
    # 即 sin(w*x+α) <= C/A
    # 首先化成标准形式 A*sin(w*x+α) <= C 的形式
    def solver(self, *args):
        trig_ineq = args[0]
        x_symbol = sympify('x')
        trig_ineq = su.stand_trig_ineq(trig_ineq)
        l, op, r = trig_ineq.sympify()
        op = op.strip()
        sim_l = trig_simplify(l)
        if not su.trig_is_simped(sim_l):
            raise Exception('Can not solve ineq')
        if sim_l != l:
            self.steps.append(['由题意可知' + BaseIneq([sim_l, op, r]).printing(), ''])
        trig_f_coef, trig_f_type, trig_f_arg, _ = su.simp_trig_info(sim_l)
        sim_l = trig_f_type(trig_f_arg)
        r = r / trig_f_coef
        if trig_f_coef < 0:
            op = co.reverse_ineq_op(op)
        self.steps.append(['所以' + BaseIneq([sim_l, op, r]).printing(), ''])
        arg_solve_set = None
        k_symbol = sympify('k')
        if trig_f_type == sin:
            if op == '<':  # sin(x) < a
                if r > 1:
                    arg_solve_set = Interval(-S.Infinity, S.Infinity)
                elif r <= -1:
                    arg_solve_set = EmptySet()
                elif -1 < r <= 1:
                    arg_solve_set = Interval.open(2 * k_symbol * pi - pi - asin(r), 2 * k_symbol * pi + asin(r))
            elif op == '<=':  # sin(x) <= a
                if r >= 1:
                    arg_solve_set = Interval(-S.Infinity, S.Infinity)
                elif r < -1:
                    arg_solve_set = EmptySet()
                elif -1 <= r < 1:
                    arg_solve_set = Interval(2 * k_symbol * pi - pi - asin(r), 2 * k_symbol * pi + asin(r))
            elif op == '>':  # sin(x) > a
                if r < -1:
                    arg_solve_set = Interval(-S.Infinity, S.Infinity)
                elif r >= 1:
                    arg_solve_set = EmptySet()
                elif -1 <= r < 1:
                    arg_solve_set = Interval.open(2 * k_symbol * pi + asin(r), 2 * k_symbol * pi + pi - asin(r))
            elif op == '>=':  # sin(x) >= a
                if r <= -1:
                    arg_solve_set = Interval(-S.Infinity, S.Infinity)
                elif r > 1:
                    arg_solve_set = EmptySet()
                elif -1 < r <= 1:
                    arg_solve_set = Interval.open(2 * k_symbol * pi + asin(r), 2 * k_symbol * pi + pi - asin(r))
            else:
                raise Exception('illegal inequality operators')
        elif trig_f_type == cos:
            if op == '<':  # cos(x) < a
                if r > 1:
                    arg_solve_set = Interval(-S.Infinity, S.Infinity)
                elif r <= -1:
                    arg_solve_set = EmptySet()
                elif -1 < r <= 1:
                    arg_solve_set = Interval.open(2 * k_symbol * pi + acos(r), 2 * k_symbol * pi + 2 * pi - acos(r))
            elif op == '<=':  # cos(x) <= a
                if r >= 1:
                    arg_solve_set = Interval(-S.Infinity, S.Infinity)
                elif r < -1:
                    arg_solve_set = EmptySet()
                elif -1 <= r < 1:
                    arg_solve_set = Interval(2 * k_symbol * pi + acos(r), 2 * k_symbol * pi + 2 * pi - acos(r))
            elif op == '>':  # cos(x) > a
                if r < -1:
                    arg_solve_set = Interval(-S.Infinity, S.Infinity)
                elif r >= 1:
                    arg_solve_set = EmptySet()
                elif -1 <= r < 1:
                    arg_solve_set = Interval.open(2 * k_symbol * pi - acos(r), 2 * k_symbol * pi + acos(r))
            elif op == '>=':  # cos(x) >= a
                if r <= -1:
                    arg_solve_set = Interval(-S.Infinity, S.Infinity)
                elif r > 1:
                    arg_solve_set = EmptySet()
                elif -1 < r <= 1:
                    arg_solve_set = Interval(2 * k_symbol * pi - acos(r), 2 * k_symbol * pi + acos(r))
            else:
                raise Exception('illegal inequality operators')
        elif trig_f_type == tan:
            if op == '<':
                arg_solve_set = Interval.open(k_symbol * pi - pi / 2, k_symbol * pi + atan(r))
            elif op == '<=':
                arg_solve_set = Interval.Lopen(k_symbol * pi - pi / 2, k_symbol * pi + atan(r))
            elif op == '>':
                arg_solve_set = Interval.open(k_symbol * pi + atan(r), k_symbol * pi + pi / 2)
            elif op == '>=':
                arg_solve_set = Interval.Ropen(k_symbol * pi + atan(r), k_symbol * pi + pi / 2)
            else:
                raise Exception('illegal inequality operators')
        elif trig_f_type == cot:
            if op == '<':
                pass
            elif op == '<=':
                pass
            elif op == '>':
                pass
            elif op == '>=':
                pass
            else:
                raise Exception('illegal inequality operators')
        if isinstance(arg_solve_set, EmptySet):
            solve_set = EmptySet()
        elif arg_solve_set.left == -S.Infinity:
            solve_set = Interval(-S.Infinity, S.Infinity)
        else:
            solve_set = su.solve_param_ineq([arg_solve_set.left, '<' if arg_solve_set.left_open else '<=', trig_f_arg,
                                             '<' if arg_solve_set.right_open else '<=', arg_solve_set.right])
        self.steps.append(['求得解集为', new_latex(solve_set)])
        self.output.append(BaseSymbolValue({x_symbol: solve_set}))
        return self


# 接不等式组
class SanJiaoBuDengShi002(BaseFunction):
    def solver(self, *args):
        if not isinstance(args[0], BaseIneqs):
            raise Exception('Type Match Error')
        ineqs = args[0].sympify()
        x_symbol = sympify('x')
        ineq_solvets = []
        for ineq in ineqs:
            ineq_solve = JieSanJiaoBuDengShi().solver(BaseIneq(ineq)).output[0].sympify().values()[0]
            if not ineq_solve == S.Reals:  # not isinstance(ineq_solve, Reals)
                ineq_solvets.append(ineq_solve)
        laste_interval = ineq_solvets[0]
        laste_interval_values = [laste_interval.left, laste_interval.right]
        k_symbol = list(laste_interval.left.free_symbols)[0]
        k_coef = laste_interval.left.coeff(k_symbol * pi)
        left_open = laste_interval.left_open
        right_open = laste_interval.right_open
        for ineq_solve in ineq_solvets[1:]:
            currnt_left = ineq_solve.left
            left_open = left_open and ineq_solve.left_open
            current_right = ineq_solve.right
            right_open = right_open and ineq_solve.right_open
            currnt_left_const = currnt_left.subs(k_symbol, 0)
            current_right_const = current_right.subs(k_symbol, 0)
            other_left_const, other_right_const = map(lambda _: _.subs(k_symbol, 0), laste_interval_values)
            if max(currnt_left_const, other_left_const) <= min(current_right_const, other_right_const):
                laste_interval_values[0] = max(currnt_left_const, other_left_const)
                laste_interval_values[1] = min(current_right_const, other_right_const)
            else:
                laste_interval_values = None
        if not laste_interval_values:
            self.output.append(BaseSymbolValue({x_symbol: EmptySet()}))
        else:
            left_value, right_value = map(lambda _: _.subs(k_symbol, 0), laste_interval_values)
            last_intl = Interval(k_coef * k_symbol * pi + left_value, k_coef * k_symbol * pi + right_value, left_open,
                                 right_open)
            self.output.append(BaseSymbolValue({x_symbol: last_intl}))
        return self


# 使用Mathematic解决
class MatchSolve(BaseFunction):
    @staticmethod
    def _sub_expr(f_expr):
        return sympify(f_expr).subs('Pi', pi).subs('C_1', 'k')

    @staticmethod
    def _sub_pi(intl):
        if isinstance(intl, Interval):
            left = intl.left
            right = intl.right
            if left != -S.Infinity:
                left = sympify(left).subs('Pi', pi).subs('C_1', 'k')
            if right != S.Infinity:
                right = sympify(right).subs('Pi', pi).subs('C_1', 'k')
            return Interval(left, right, intl.left_open, intl.right_open)
        return intl

    def solver(self, *args):
        variable = None
        _new_ineqs = []
        if len(args) == 1 and isinstance(args[0], BaseIneq):
            ineq = args[0].sympify()
            l_expr, op, r_expr = ineq
            _f = su.i_trig_simplify(l_expr - r_expr)
            _new_ineqs.append([_f, op, '0'])
            variable = co.ineqs_symbs(ineq)[0]
        elif len(args) == 1 and isinstance(args[0], BaseIneqs):
            ineqs = args[0].sympify()
            for _ineq in ineqs:
                l_expr, op, r_expr = _ineq
                _f = su.i_trig_simplify(l_expr - r_expr)
                _new_ineqs.append([_f, op, '0'])
            _symbs = co.ineqs_symbs(ineqs)
            if len(_symbs) > 1:
                variable = sympify('x')
            else:
                variable = _symbs[0]
        elif len(args) == 2 and isinstance(args[0], BaseIneq) and (isinstance(args[1], BaseVariables) or
                                                                   isinstance(args[1], BaseVariable)):
            if isinstance(args[1], BaseVariables):
                variable = args[1].sympify()[0]
            else:
                variable = args[1].sympify()
            ineq = args[0].sympify()
            l_expr, op, r_expr = ineq
            _f = su.i_trig_simplify(l_expr - r_expr)
            _new_ineqs.append([_f, op, '0'])
        elif len(args) == 2 and isinstance(args[0], BaseIneqs) and isinstance(args[1], BaseVariable):
            variable = args[1].sympify()
            _new_ineqs.extend(args[0].sympify())
        symbs = co.ineqs_symbs(_new_ineqs)
        solve_result = MathematicaSolve().solver(BaseIneqs(_new_ineqs), BaseVariables([variable])).output[0].value
        if len(symbs) > 1:  # 含参的不等式
            param_symb = sympify(list(set(symbs) - {variable})[0])
            from mathsolver.functions.budengshi.jiebudengshi import BaseParamerInterval
            _value = {'var': variable, 'paramer_var': param_symb, 'solveset': None}
            _solve_set = []
            for combin_key, symbs_intls in solve_result.items():
                for symbs_intl in symbs_intls:
                    _d = dict(zip(combin_key, symbs_intl))
                    one_result = map(lambda _: _d[_], [param_symb, variable])
                    pair_result = []
                    for _r in one_result:
                        if isinstance(_r, Interval):
                            pair_result.append(MatchSolve._sub_pi(_r))
                        elif isinstance(_r, Expr):
                            pair_result.append(list(FiniteSet(MatchSolve._sub_expr(_r)))[0])
                        elif isinstance(_r, FiniteSet):
                            pair_result.append(list(_r)[0])
                        else:
                            pair_result.append(_r)
                    pair_result = tuple(pair_result)
                    _solve_set.append(pair_result)
            _solve_set = tuple(_solve_set)
            _value['solveset'] = _solve_set
            self.output.append(BaseParamerInterval(_value))
            return self
        variable = sympify(variable)
        if len(solve_result.items()) == 1:
            symbs_key, symbs_intervals = list(solve_result.items())[0]
        elif len(solve_result.items()) == 2:
            symbs_key, symbs_intervals = list(filter(lambda _: co.iterable(_[0]) and len(_[0]) == 2,
                                                     solve_result.items()))[0]
        if isinstance(symbs_intervals, FiniteSet):
            symbs_intervals = list(symbs_intervals)
        if isinstance(symbs_intervals, Interval):
            self.output.append(BaseSymbolValue(solve_result))
            return self
        intervals = []
        for _, x_symb_intl in symbs_intervals:
            if isinstance(x_symb_intl, Interval):
                intervals.append(MatchSolve._sub_pi(x_symb_intl))
            elif isinstance(x_symb_intl, Expr):
                intervals.append(FiniteSet(MatchSolve._sub_expr(x_symb_intl)))
            elif isinstance(x_symb_intl, FiniteSet):
                intervals.append(FiniteSet(*map(MatchSolve._sub_expr, list(x_symb_intl))))
        last_intl = co.intervals_union(intervals)
        self.output.append(BaseSymbolValue({variable: last_intl}))
        return self


class JieSanJiaoBuDengShi(BaseFunction):
    CLS = [MatchSolve, ]

    def solver(self, *args):
        solve_r = None
        for cl in JieSanJiaoBuDengShi.CLS:
            try:
                solve_r = cl(verbose=True)
                solve_r.known = self.known
                solve_r = solve_r.solver(*args)
                solve_r.label.add('解三角不等式')
                break
            except Exception:
                solve_r = None
        if not solve_r:
            raise Exception('try fail')
        return solve_r


if __name__ == '__main__':
    pass
